[AGC023F]01 on tree

2019-12-06
Atcoder

题意

给出一棵权值为01的树,找到一个拓扑序列,使得权值序列中逆序对对数最少

求最小值

题解

考虑两个拓扑序的合并,令$a_1,b_1,a_2,b_2$分别为序列1,2中01的个数

当$b_1\cdot a_2\leq a_1\cdot b_2$即$\frac{b_1}{a_1}\leq\frac{b_2}{a_2}$时,序列1在前面更优

满足排序性质,用堆维护

每次找最小的序列和父亲的序列合并一定最优,局部最优必定导致全局最优

调试记录

1不能再合并,不要加进去

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <cstdio>
#include <queue>
#define LL long long
const int maxn = 2e5 + 5;
using namespace std;
int f[maxn];
int getf(int x){return x == f[x] ? x : f[x] = getf(f[x]);}
struct A{
int a, b, id;
bool operator<(A x)const{
return 1ll * b * x.a > 1ll * a * x.b;
}
bool operator!=(A x)const{
return a != x.a || b != x.b;
}
}a[maxn]; LL ans = 0;
priority_queue <A> q;
int n, anc[maxn];
int main(){
scanf("%d", &n);
for (int i = 2; i <= n; i++) scanf("%d", anc + i);
for (int val, i = 1; i <= n; i++){
scanf("%d", &val);
f[i] = i;
if (val == 0) a[i] = (A){1, 0, i};
else a[i] = (A){0, 1, i};
if (i != 1) q.push(a[i]);
}
while (!q.empty()){
A t = q.top(); q.pop();
int cur = getf(t.id);
if (a[cur] != t) continue;
int fa = getf(anc[cur]);
ans += 1ll * a[fa].b * a[cur].a;
a[fa].a += a[cur].a;
a[fa].b += a[cur].b;
f[getf(cur)] = f[getf(fa)];
if (fa != 1) q.push(a[fa]);
}
printf("%lld\n", ans);
return 0;
}